package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.Constants;
import com.zzyl.constant.SuperConstant;
import com.zzyl.constant.UserCacheConstant;
import com.zzyl.dto.AddUserDto;
import com.zzyl.dto.LoginDto;
import com.zzyl.dto.UpdateUserDto;
import com.zzyl.dto.UserPageQueryDto;
import com.zzyl.entity.Resource;
import com.zzyl.entity.User;
import com.zzyl.entity.UserRole;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.RoleMapper;
import com.zzyl.mapper.SysUserMapper;
import com.zzyl.mapper.UserRoleMapper;
import com.zzyl.properties.JwtTokenManagerProperties;
import com.zzyl.properties.SecurityConfigProperties;
import com.zzyl.service.ResourceService;
import com.zzyl.service.UserService;
import com.zzyl.utils.JwtUtil;
import com.zzyl.utils.NoProcessing;
import com.zzyl.utils.StringUtils;
import com.zzyl.vo.UserListVo;
import com.zzyl.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.zzyl.constant.SuperConstant.DATA_STATE_0;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;


    @Autowired
    private JwtTokenManagerProperties jwtTokenManagerProperties;

    @Autowired
    ResourceService resourceService;

    @Autowired
    private SecurityConfigProperties securityConfigProperties;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 登录
     */
    @Override
    public UserVo login(LoginDto loginDto) {
        // 先查库 判断有无此角色 没有抛异常
        UserVo userVo = sysUserMapper.selectByName(loginDto.getUsername());
        if (BeanUtil.isEmpty(userVo)) {
            throw new BaseException(BasicEnum.USER_LOGIN_NOT_EXISTS);
        }
        // 查看角色状态  是否被禁用
        String dataState = userVo.getDataState();
        if (Objects.equals(dataState, SuperConstant.DATA_STATE_1)) {
            throw new BaseException(BasicEnum.USER_LOGIN_DISABLED);
        }
        // 没被禁用 查密码是否正确 BCrypt
        boolean checkpw = BCrypt.checkpw(loginDto.getPassword(), userVo.getPassword());
        if (!checkpw) {
            throw new BaseException(BasicEnum.USER_LOGIN_PWD_ERROR);
        }
        // 不再使用下面的方法获取resource放到redis了
        // 使用resource表的label字段  登录的时候写入到redis中
        // 然后使用aop  给要鉴权的接口添加自定义注解 然后aop去redis中获取当前角色的label列表
        // 如果列表中包含要访问的接口 说明对这个接口有权限 如果不包含 说明没权限
        List<Resource> resourceList = resourceService.getByUserId(userVo.getId());
        Set<String> perms = new HashSet<>();
        if (CollectionUtil.isNotEmpty(resourceList)) {
            for (Resource resource : resourceList) {
                String perm = resource.getLabel();
                if (perm != null && !perm.isEmpty()) {
                    perms.add(perm);
                }
            }
        }
        stringRedisTemplate.opsForSet().add(UserCacheConstant.USER_RESOURCE + userVo.getId(),
                perms.toArray(new String[0]));

        /*// 查找角色的资源  写入到redis中
        // 先获取资源白名单
        List<String> publicAccessUrls = securityConfigProperties.getPublicAccessUrls();
        // 然后去数据库获取角色的资源列表
        // List<Resource> resourceList = resourceMapper.selectByUserId(userVo.getId(),null,null);
        List<Resource> resourceList = resourceService.selectByUserId(userVo.getId());
        Set<String> urlSet = new HashSet<>();
        if (!publicAccessUrls.isEmpty()) {
            urlSet.addAll(publicAccessUrls);
        }
        if (!resourceList.isEmpty()) {
            Set<String> collect =
                    resourceList.stream().map(Resource::getRequestPath).filter(Objects::nonNull).collect(Collectors
                    .toSet());
            urlSet.addAll(collect);
        }
        stringRedisTemplate.opsForSet().add(UserCacheConstant.USER_RESOURCE + userVo.getId(),
                new ArrayList<>(urlSet).toArray(new String[0]));*/


        // 以上判断都正确  登陆成功 生成token 并且返回用户信息
        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.JWT_USERID, userVo.getId());
        claims.put(Constants.JWT_USERNAME, userVo.getUsername());
        userVo.setUserToken(JwtUtil.createJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(),
                jwtTokenManagerProperties.getTtl(), claims));
        userVo.setPassword("");
        return userVo;
    }
    /*@Override
    public UserVo login(LoginDto dto) {
        // 根据用户名查库
        UserVo userVo = sysUserMapper.selectByName(dto.getUsername());
        // 判断用户是否存在
        if(userVo == null){
            // 如果不存在，return
            throw new BaseException(BasicEnum.DATA_NOT_EXIST);
        }
        // 如果存在， 判断状态是否禁用
        String dataState = userVo.getDataState();
        if(Objects.equals(dataState, DATA_STATE_1)){
            // 如果是禁用，return
            throw new BaseException(BasicEnum.USER_STATE_1);
        }
        // 如果不是禁用，校验密码，BCrypt
        String passwordDB = userVo.getPassword();
        if(!BCrypt.checkpw(dto.getPassword(), passwordDB)){
            // 如果密码错误，return
            throw new BaseException(BasicEnum.PASSWORD_ERROR);
        }
        // 查询用户的权限
        List<Resource> resourceList = resourceService.getByUserId(userVo.getId());
        // 获取白名单
        List<String> publicAccessUrls = securityConfigProperties.getPublicAccessUrls();
        Set<String> urlSet = new HashSet<>();
        if(!CollectionUtils.isEmpty(resourceList)){
            Set<String> urls = resourceList.stream().map(Resource::getRequestPath).filter(Objects::nonNull).collect(Collectors.toSet());
            urlSet.addAll(urls);
        }
        if(!CollectionUtils.isEmpty(publicAccessUrls)){
            urlSet.addAll(publicAccessUrls);
        }
        // 保存到redis
        redisTemplate.opsForSet().add(USER_RESOURCE_LIST+userVo.getId(), new ArrayList<>(urlSet).toArray(new String[0]));
        // 如果密码正确，生成token，return
        Map<String,Object> map = new HashMap<>();
        map.put(JWT_USERID,userVo.getId());
        map.put(JWT_USERNAME,dto.getUsername());
        userVo.setUserToken(JwtUtil.createJWT(jwtProperties.getBase64EncodedSecretKey(), jwtProperties.getTtl(),map));
        userVo.setPassword("");
        return userVo;
    }*/



    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(AddUserDto dto) {
        // 判断重复
        User userDB = sysUserMapper.selectByEmail(dto.getEmail());
        if(userDB != null){
            throw new BaseException(BasicEnum.DATA_EXIST);
        }
        // 保存user
        User user = BeanUtil.copyProperties(dto, User.class);
        String hashpw = BCrypt.hashpw("123456");
        user.setPassword(hashpw);
        user.setUsername(dto.getEmail());
        user.setNickName(dto.getRealName());
        sysUserMapper.insert(user);
        // 保存角色
        if(!CollectionUtils.isEmpty(dto.getRoleVoIds())){
            List<UserRole> userRoles = dto.getRoleVoIds().stream().map(r -> {
                UserRole ur = new UserRole();
                ur.setRoleId(Long.parseLong(r));
                ur.setUserId(user.getId());
                ur.setDataState(DATA_STATE_0);
                return ur;
            }).collect(Collectors.toList());
            userRoleMapper.insertBatch(userRoles);
        }

    }

    @Override
    public PageResponse<UserListVo> page(Integer pageNum, Integer pageSize, UserPageQueryDto dto) {
        PageHelper.startPage(pageNum == null ? 1 : pageNum, pageSize == null ? 10 : pageSize);
        // 单独处理下部门编号
        if (!StringUtils.isEmpty(dto.getDeptNo())) {
            dto.setDeptNo(NoProcessing.processString(dto.getDeptNo()));
        }
        Page<UserListVo> page = sysUserMapper.pageQuery(dto);
        List<UserListVo> result = page.getResult();
        if (!CollectionUtils.isEmpty(result)) {
            result.stream().forEach((vo) -> {
                vo.setRoleVoIds(StringUtils.isEmpty(vo.getRoleIds()) ? new ArrayList<>() : Arrays.asList(vo.getRoleIds().split(",")));
                vo.setRoleLabels(StringUtils.isEmpty(vo.getRoleNames()) ? new ArrayList<>() : Arrays.asList(vo.getRoleNames().split(",")));
            });
//            for (UserListVo userListVo:result){
//                List<String> roleNames = roleMapper.selectRoleNamesByUserId(userListVo.getId());
//                userListVo.setRoleLabels(roleNames);
//            }

        }
        return PageResponse.of(page, UserListVo.class);
    }



/*
    @Override
    public PageResponse<UserListVo> page(Integer pageNum, Integer pageSize, UserPageQueryDto dto) {
        PageHelper.startPage(pageNum == null ? 1 : pageNum, pageSize == null ? 10 : pageSize);
        // 单独处理下部门编号
        if (!StringUtils.isEmpty(dto.getDeptNo())) {
            dto.setDeptNo(NoProcessing.processString(dto.getDeptNo()));
        }
        Page<UserListVo> page = sysUserMapper.pageQuery(dto);
        List<UserListVo> result = page.getResult();
        if (!CollectionUtils.isEmpty(result)) {
            result.stream().forEach((vo) -> {
                vo.setRoleVoIds(StringUtils.isEmpty(vo.getRoleIds()) ? new ArrayList<>() : Arrays.asList(vo.getRoleIds().split(",")));
                vo.setRoleLabels(StringUtils.isEmpty(vo.getRoleNames()) ? new ArrayList<>() : Arrays.asList(vo.getRoleNames().split(",")));
            });
//            for (UserListVo userListVo:result){
//                List<String> roleNames = roleMapper.selectRoleNamesByUserId(userListVo.getId());
//                userListVo.setRoleLabels(roleNames);
//            }
        }
        return PageResponse.of(page, UserListVo.class);
    }
*/

    /*@Override
    public PageResponse<UserListVo> page(Integer pageNum, Integer pageSize, UserPageQueryDto dto) {
        PageHelper.startPage(pageNum == null ? 1 : pageNum, pageSize == null ? 10 : pageSize);
        // 单独处理下部门编号
        if (!StringUtils.isEmpty(dto.getDeptNo())) {
            dto.setDeptNo(NoProcessing.processString(dto.getDeptNo()));
        }
        // 查询用户、部门、职位
        Page<UserListVo> page = sysUserMapper.pageQuery2(dto);
        List<UserListVo> result = page.getResult();
        // 单独处理角色
        if (!CollectionUtils.isEmpty(result)) {
            // 拿到所有的用户id
            List<Long> userIds = result.stream().map(UserListVo::getId).collect(Collectors.toList());
            // 根据用户ids批量查用户的角色
            List<RoleWithUserId> roles = roleMapper.selectRoleListByUserIds(userIds);
            if (!CollectionUtils.isEmpty(roles)) {
                // 把角色的list转化成了map，key：userId， value：用户的角色列表
                Map<Long, List<RoleWithUserId>> map = roles.stream().collect(Collectors.groupingBy(RoleWithUserId::getUserId));
                for (UserListVo userListVo : result) {
                    //根据用户id拿到所有的角色
                    List<RoleWithUserId> roleWithUserIds = map.get(userListVo.getId());
                    if (!CollectionUtils.isEmpty(roleWithUserIds)) {
                        // 把bean对象转化成string
                        userListVo.setRoleLabels(roleWithUserIds.stream().map(RoleWithUserId::getRoleName).collect(Collectors.toList()));
                        userListVo.setRoleVoIds(roleWithUserIds.stream().map(RoleWithUserId::getId).map(String::valueOf).collect(Collectors.toList()));
                    }
                }
            }
        }
        return PageResponse.of(page, UserListVo.class);
    }*/


    /**
     * 更新用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(UpdateUserDto dto) {
        User judgeEmailRepeat = sysUserMapper.selectByEmail(dto.getEmail());
        if (!Objects.equals(judgeEmailRepeat.getId(), dto.getId())) {
            throw new BaseException(BasicEnum.EMAIL_ALREADY_EXISTS);
        }
        User judgeRealNameRepeat = sysUserMapper.selectByRealName(dto.getRealName());
        if (!Objects.equals(judgeRealNameRepeat.getId(), dto.getId())) {
            throw new BaseException(BasicEnum.REAL_NAME_ALREADY_EXISTS);
        }
        User user = BeanUtil.copyProperties(dto, User.class);
        sysUserMapper.update(user);
        userRoleMapper.deleteByUserId(dto.getId());
        if (CollectionUtil.isEmpty(dto.getRoleVoIds())) {
            return;
        }
        List<UserRole> userRoleList = dto.getRoleVoIds().stream().map(aLong -> new UserRole(dto.getId(), aLong,
                SuperConstant.DATA_STATE_0)).collect(Collectors.toList());
        userRoleMapper.insertBatch(userRoleList);
    }

    /**
     * 删除用户
     */
    @Override
    public void deleteUser(Long userId) {
        User judgeIfEnable = sysUserMapper.selectByUserId(userId);
        if (Objects.equals(judgeIfEnable.getDataState(), SuperConstant.DATA_STATE_0)) {
            throw new BaseException(BasicEnum.USER_DELETE_ERROR);
        }
        sysUserMapper.deleteByUserId(userId);
        userRoleMapper.deleteByUserId(userId);
    }

    /**
     * 启用或禁用用户
     */
    @Override
    public void enableOrDisableUser(Long userId, String status) {
        User user = new User();
        user.setId(userId);
        user.setDataState(status);
        sysUserMapper.update(user);
    }

    /**
     * 重置密码
     */
    @Override
    public void resetPassword(Long userId) {
        User user = new User();
        user.setId(userId);
        user.setPassword(BCrypt.hashpw("888itcast.CN764%..."));
        sysUserMapper.update(user);
    }
}
